/***********************************************/
/* UnixDomainSocket.c                          */
/* Inspired by J-BUDS version 1.0              */
/* See COPYRIGHT file for license details      */
/***********************************************/
#include "UnixNetlinkSocket.h"

#include <jni.h>
#include <asm/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <netinet/in.h>
 
#include <linux/netlink.h>
#include <linux/rtnetlink.h>


#define MAX_PAYLOAD 1024  /* maximum payload size*/
long sequence_number=0;

#define ASSERTNOERR(cond, msg) do { \
    if (cond) { fprintf(stderr, "[%d] ", errno); perror(msg); return -1; }} while(0)

JNIEXPORT jint JNICALL
Java_ate_UnixNetlinkSocket_nativeOpen(JNIEnv * jEnv,
                            jclass jClass)
{
    int sock = socket(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE);
    struct sockaddr_nl addr;

    memset((void *)&addr, 0, sizeof(addr));

    if (sock<0)
        return sock;
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = 0;
    addr.nl_groups = RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR;
    
    if (bind(sock,(struct sockaddr *)&addr,sizeof(addr))<0)
        return -1;

    
    return sock;
}

JNIEXPORT jbyteArray JNICALL
Java_ate_UnixNetlinkSocket_nativeReadevent(JNIEnv * jEnv,
                              jclass jClass,
                              jint jSocketFileHandle
                              )
{
    int status;
    int ret = 0;
    char buf[4096];
    struct iovec iov = { buf, sizeof buf };
    struct sockaddr_nl snl;
    struct msghdr msg = { (void*)&snl, sizeof snl, &iov, 1, NULL, 0, 0};
    struct nlmsghdr *h;
    jbyteArray result=NULL;
    jbyte tmp[255];
    int cnt=0;
    
    status = recvmsg(jSocketFileHandle, &msg, 0);

    if(status < 0)
    {
        /* Socket non-blocking so bail out once we have read everything */
        if (errno == EWOULDBLOCK || errno == EAGAIN)
            return result;

        /* Anything else is an error */
        printf("read_netlink: Error recvmsg: %d\n", status);
        perror("read_netlink: Error: ");
        return result;
    }
        
    if(status == 0)
    {
        printf("read_netlink: EOF\n");
    }
		
    /* We need to handle more than one message per 'recvmsg' */
    for(h = (struct nlmsghdr *) buf; NLMSG_OK (h, (unsigned int)status); h = NLMSG_NEXT (h, status))
    {
        tmp[cnt]=h->nlmsg_type;        
        if (tmp[cnt] == NLMSG_DONE || tmp[cnt] == NLMSG_ERROR )
            break;                   
        switch (tmp[cnt])
	{
	        case RTM_NEWADDR:
	            printf("msg_handler: RTM_NEWADDR\n");
	            break;
	        case RTM_DELADDR:
	            printf("msg_handler: RTM_DELADDR\n");
	            break;
	        case RTM_NEWROUTE:
	            printf("msg_handler: RTM_NEWROUTE\n");
	            break;
	        case RTM_DELROUTE:
	            printf("msg_handler: RTM_DELROUTE\n");
	            break;
	        case RTM_NEWLINK:
	            printf("msg_handler: RTM_NEWLINK\n");
	            struct ifinfomsg *ifi;
	            ifi = NLMSG_DATA(h);
		    cnt++;
                    tmp[cnt]= ifi->ifi_index;			
		    printf("netlink_link_state: Link %d %s\n", ifi->ifi_index, 
			           (ifi->ifi_flags & IFF_RUNNING)?"Up":"Down");
	            break;
	        case RTM_DELLINK:
	            printf("msg_handler: RTM_DELLINK\n");
	            break;
	        default:
	            printf("msg_handler: Unknown netlink nlmsg_type %d\n",
	                   tmp[cnt]);
	            break;
	}
	cnt++; 
    }
    
    result=(*jEnv)->NewByteArray(jEnv,cnt+1);
		(*jEnv)->SetByteArrayRegion(jEnv,result,0,cnt+1,tmp);
		
    return result;
}

JNIEXPORT jint JNICALL
Java_ate_UnixNetlinkSocket_nativeClose(JNIEnv * jEnv,
                               jclass jClass,
                               jint jSocketFileHandle)
{
    return close(jSocketFileHandle);
}

